package com.jtfr.core;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 执行 SQL 的Handler
 * @author ckm
 *
 */
public class SqlExecuteHandler implements InvocationHandler {
	
	private static Logger LOGGER = LoggerFactory.getLogger(SqlExecuteHandler.class);
 
    public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
        // 传入的要被执行的接口方法，通过获取接口名和方法名，然后获取要MapperInfo信息。
        MapperMethodInfo mapperMethodInfo = getMapperInfo(method);
 
        // 通过MapperInfo和传入的参数，执行Sql语句，并且返回结果。
        return executeSql(mapperMethodInfo, params);
    }
    
    /**
     * 通过 接口方法 获取接口对应的 MapperMethodInfo 对象
     * @param method
     * @return
     * @throws Exception
     */
    private MapperMethodInfo getMapperInfo(Method method) throws Exception {
        MapperMethodInfo mapperInfo = MapperHandler.getSqlMapperHolder().getMapperInfo(method.getDeclaringClass().getName(), method.getName());
        if (mapperInfo == null) {
        	LOGGER.error("Mapper not found for method: " + method.getDeclaringClass().getName() + "." + method.getName());
            throw new Exception("Mapper not found for method: " + method.getDeclaringClass().getName() + "." + method.getName());
        }
        return mapperInfo;
    }
 
    private Object executeSql(MapperMethodInfo mapperMethodInfo, Object[] params)
            throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Object result = null;
        Connection connection = ConnectionManager.get();
        LOGGER.info("connection : "+connection);
        PreparedStatement pstat = connection.prepareStatement(mapperMethodInfo.getSql());
        // params 不为 null 才遍历填充数据，没有参数时候，代理传入的就是 null
        if (params != null) {
        	for (int i = 0; i < params.length; i++) {
        		pstat.setObject(i + 1, params[i]);
        	}
		}
        // 判断是否是查询语句
        if (mapperMethodInfo.getQueryType() == QueryType.SELECT) {
            ResultSet rs = pstat.executeQuery();
            // 判断是否有值, 没有就退出
            if (!rs.next()) {
				return result;
			}
            // 将查询结果映射为java类或基本数据类型
            // 基本数据类型 还不全面，等看了 Mybatis的反射再来写一下。
            if (rs.getMetaData().getColumnCount() == 1) {
                switch (mapperMethodInfo.getResultType()) {
                    case "int":
                        result = rs.getInt(1);
                        break;
                    default:
                        result = rs.getString(1);
                }
            } else {
            	// 获取返回结果类型，反射创建对象。
                Class<?> resultTypeClass = Class.forName(mapperMethodInfo.getResultType());
                Object inst = resultTypeClass.newInstance();
                for (Field field : resultTypeClass.getDeclaredFields()) {
                    String setterName = "set" +
                            field.getName().substring(0, 1).toUpperCase() +
                            field.getName().substring(1);
                    Method md;
 
                    switch (field.getType().getSimpleName()) {
                        case "int":
                        	// 获取方法
                            md = resultTypeClass.getMethod(setterName, new Class[]{int.class});
                            // 反射执行方法对 field进行赋值。
                            md.invoke(inst, rs.getInt(field.getName()));
                            break;

                        case "String":
                            md = resultTypeClass.getMethod(setterName, new Class[]{String.class});
                            md.invoke(inst, rs.getString(field.getName()));
                            break;
                        
                        default:
                            md = resultTypeClass.getMethod(setterName, new Class[]{String.class});
                            md.invoke(inst, rs.getString(field.getName()));
                    }
                }
                result = inst;
            }
        } else {
            result = pstat.executeUpdate();
        }
        pstat.close();
        ConnectionManager.set(connection);
        return result;
    }
 
}
